/*Include GBA common Header files*/
#include"./common/gba.h"                // GBA DEFINITION
#include"./common/screenmode.h"         // Screen - Graphics DEFINITION
#include"./common/bg.h"                 // Background DEFINITION
#include"./common/sprite.h"             // Sprite DEFINITION
#include"./common/keypad.h"             // Keys DEFINITION
#include"./common/interrupt.h"          // Interrupt DEFINITION
#include"./common/dma.h"                // DMA DEFINITION
#include"./common/timers.h"             // Timers DEFINITION
#include"./common/uart.h"               // Sound DEFINITION
#include"./common/util.h"               // Utility DEFINITION
/*Include User-defined Header files*/
#include"arrow.h"
#include"ball.h"
#include "egg.h"

/*Include Third-party Header files*/
#include"stdio.h"
#include"math.h"

/*Some measurement DEFINITION*/
#define FIXED s32
#define PI 3.14159
#define RADIAN(n) 		(((float)n)/(float)180*PI)

/*Declare our function prototypes*/
void InitializeSprites(void); // Init the sprites (data, attributes)
void SetupFirstSprite(void); // Setup the 1st sprite (arrow)
void WaitForVsync(void); // Keep synchronous sprites processing
void CopyOAM(void); // Copy sprites array to OAM (real memory)
void MoveSprite(OAMEntry* sp, int x, int y); // Mote the sprite to coordination (x,y)
void RotateSpriteByAngle(int rotDataIndex, int angle, FIXED x_scale,
		FIXED y_scale); // Rotate the sprite by angle, scales
void GetInput(void); // Get and Process keyboard input

/* The origin position of the Arrow (8x16) */
s16 x = 100; // 8 pixels for arrow's width and 80 for text display
s16 y = 160 - 16; // 16 pixels for arrow's length

/* Attributes of Ball (8x8) */
s16 xBall; // x of Ball
s16 yBall; // y of Ball
u16 _stateOfBall; // State of Ball

/*Tile Index*/
u16 _tileIndex = 0; // For sprite 0

/*For checking arrow movement state*/
u16 _isArrowXIncreased = 1; // Flag for proper arrow movement
u16 _isIncAngle = 1; // Flag for proper arrow rotation

/*For rotation*/FIXED angle = 0; // Angle
FIXED zoom = 1 << 8; // Zoom
FIXED SIN[360]; // SIN array
FIXED COS[360]; // COS array

/*Debug stream*/
char buffer[100];
/*Looping variable*/
int i = 0;

/* Init value for egges*/
int xEgges[] = { 3, 19, 30, 40, 120 };
int yEgges[] = { 1, 10, 15, 10, 40 };
/*Main entry point*/
int main(void) {
	sprintf(buffer, "Hello Prof. Thong", REG_DISPCNT);
	debug(buffer);

	/*For generating SIN COS array from 0 to 360 angle
	 Compute SIN and COS Array-Items and Cast to FIXED*/
	for (i = 0; i < 360; i++) {
		SIN[i] = (FIXED) (sin(RADIAN(i)) * 256);
		COS[i] = (FIXED) (cos(RADIAN(i)) * 256);
	}

	/*Set GBA Graphics MODE
	 Mode 2 and enable sprites and 2D mapping*/
	SetMode(MODE_2 | OBJ_ENABLE | OBJ_MAP_1D);

	/*Copy arrow palette to Object Palette Memory - ((u16*)0x5000200)*/
	for (i = 0; i < 256; i++)
		OBJPaletteMem[i] = arrowPalette[i];

	/*Set-up all 128 sprites to offscreen*/
	InitializeSprites();

	//*****Copy data

	//Arrow
	//Copy 2 byte at one time 
	//Image = 8x16 pixel. 1 pixel need 1 byte for expressing 256 colors. 8x16= 128 bytes => copy 128/2 items
	//*********
	//[Number of item to copy] = [Number of pixel-byte]/2
	//******
	u16 lIndexOAMData = -1;
	for (i = 0; i < 8 * 8; i++) { // DMA
		lIndexOAMData += i;
		//	Log(lIndexOAMData);
		OAMData[i] = arrowData[i];
	}//end index loop

	//Ball
	for (i = 0; i < 8 * 4; i++) {
		lIndexOAMData += i;
		OAMData[i + 64] = ballData[i];
	}

	//Egg
	for (i = 0; i < 8 * 4; i++) {

		lIndexOAMData += i;
		OAMData[i + 96] = eggData[i];
	}
	//**Set property
	/*Set-up the first sprite (Arrow)*/
	sprites[0].attribute0 = COLOR_256 | 0x8000 | ROTATION_FLAG | y;
	sprites[0].attribute1 = 0x0000 | ROTDATA(0) | x;

	/* Sprite is represented by tiles. Start at title 0.  
	 Tile 0 is the first sprite data memory location.*/
	sprites[0].attribute2 = 0;

	//SetupFirstSprite();

	//Ball sprite
	yBall = y - 8;
	xBall = x;

	sprites[1].attribute0 = COLOR_256 | 0x0000 | yBall;
	sprites[1].attribute1 = 0x0000 | ROTDATA(0) | xBall;
	sprites[1].attribute2 = 4;

	for (i = 0; i < 5; i++) {
		sprites[i + 2].attribute0 = COLOR_256 | 0x0000 | yEgges[i];
		sprites[i + 2].attribute1 = 0x0000 | ROTDATA(0) | xEgges[i];
		sprites[i + 2].attribute2 = 6;
	}
	///Main Game loop
	while (1) {
		GetInput();
		//get input changes the x and y based on input
		/*if(_isArrowXIncreased)
		 {
		 x++;
		 if (x>200)
		 {
		 _isArrowXIncreased=0;
		 x--;
		 }
		 }
		 else
		 {
		 x--;
		 if(x<1)
		 {
		 x=1;
		 _isArrowXIncreased=1;
		 }

		 }*/

		/*if (_isIncAngle)
		 {
		 angle++;
		 if (angle>180)
		 {
		 _isIncAngle=0;
		 angle=180;
		 }
		 }
		 else
		 {
		 angle--;
		 if (angle<0)
		 {
		 angle=0;
		 _isIncAngle=1;
		 }
		 }
		 */

		//changes sprite atributes based on new x,y

		//Ball Ready for shouting
		if (_stateOfBall == 0) {
			// set ball position to arrow top
			xBall = x;
			yBall = y - 8;
		}
		//Ball moving
		else {
			yBall--;
			if (yBall < 0) {
				_stateOfBall = 0;
			}
		}

		MoveSprite(&sprites[0], x, y); // Arrow
		MoveSprite(&sprites[1], xBall, yBall); // Ball
		RotateSpriteByAngle(0, angle, zoom, zoom);

		CheckTarget();

		WaitForVsync(); //waits for the screen to stop drawing
		CopyOAM(); //Copies our sprite array into OAM.
	}
}

////Set sprites to off screen (cannot be seen)

void InitializeSprites(void) {
	int i;
	for (i = 0; i < 128; i++) {
		sprites[i].attribute0 = 160; //y to > 159
		sprites[i].attribute1 = 240; //x to > 239
	}
}

void SetupFirstSprite() {
	/*Set-up sprite atributes for the first sprite: Arrow
	 COLOR_256: 256 colors
	 0x8000 x 0x0000: 10 x 00 => 8x16 (shape - height x size - width)
	 ROTATION_FLAG: To enable rotation
	 y: 8 bit for height - 256 values
	 */

}

//wait for the screen to stop drawing
void WaitForVsync(void) {

	while (REG_VCOUNT < 160)
		; // after refresh 160 line => draw new
}

///Copy our sprite array to OAM
void CopyOAM(void) {
	u16 i;
	u16* temp;
	temp = (u16*) sprites;
	for (i = 0; i < 128 * 4; i++) { // => should use DMA
		OAM[i] = temp[i];
	}
}

///Test for key presses
void GetInput(void) {
	if (!(*KEYS & KEY_UP)) {
		//y--;
		if (_stateOfBall == 0) { // 0: fixed, not yet moved
			_stateOfBall = 1; // 1: moving
		}

	}
	if (!(*KEYS & KEY_DOWN)) {
		//y++;
	}
	if (!(*KEYS & KEY_LEFT)) {
		x--;
		if (x < 0)
			x = 0;
	}
	if (!(*KEYS & KEY_RIGHT)) {
		x++;
		if (x > 152)
			x = 152;
	}
	if (!(*KEYS & KEY_L)) {
		angle--;
		if (angle < 0)
			angle = 359;
	}
	if (!(*KEYS & KEY_R)) {
		angle++;
		if (angle > 359)
			angle = 0;
	}
	if (!(*KEYS & KEY_A)) {
		zoom--;
	}
	if (!(*KEYS & KEY_B)) {
		zoom++;

	}
	if (!(*KEYS & KEY_START)) {
		if (!(sprites[0].attribute0 & SIZE_DOUBLE))//set or clear size double
		{ //needs if to keep from holding down the start
			sprites[0].attribute0 |= SIZE_DOUBLE;
			x -= 32; //size double shifts the sprite center by half the width and height this just
			y -= 32; //adjusts for it.
		}
	}
	if (!(*KEYS & KEY_SELECT)) {
		if ((sprites[0].attribute0 & SIZE_DOUBLE)) {
			sprites[0].attribute0 &= ~SIZE_DOUBLE;
			x += 32;
			y += 32;
		}
	}
}
void CheckTarget() {
	int i;
	for (i = 0; i < 5; i++) {
		//If ball & egg meet together.
		if (abs(xBall - xEgges[i]) < 8 & abs(yBall - yEgges[i]) < 8) {
			//move sprite out  - off screen
			MoveSprite(&sprites[i + 2], 240, 160);
			xEgges[i] = 241; // prevent meeting again
			_stateOfBall = 0;
		}
	}

}
//move the sprite
void MoveSprite(OAMEntry* sp, int x, int y) {
	if (x < 0) //if it is off the left corect
		x = 512 + x;
	if (y < 0) //if off the top corect
		y = 256 + y;

	sp->attribute1 = sp->attribute1 & 0xFE00; //clear the old x value
	sp->attribute1 = sp->attribute1 | x;

	sp->attribute0 = sp->attribute0 & 0xFF00; //clear the old y value
	sp->attribute0 = sp->attribute0 | y;
}
void RotateSpriteByAngle(int rotDataIndex, int angle, FIXED x_scale,
		FIXED y_scale) {
	FIXED pa, pb, pc, pd;

	pa = ((x_scale) * COS[angle]) >> 8; //(do my fixed point multiplies and shift back down)
	pb = ((y_scale) * SIN[angle]) >> 8;
	pc = ((x_scale) * -SIN[angle]) >> 8;
	pd = ((y_scale) * COS[angle]) >> 8;

	rotData[rotDataIndex].pa = pa; //put them in my data struct
	rotData[rotDataIndex].pb = pb;
	rotData[rotDataIndex].pc = pc;
	rotData[rotDataIndex].pd = pd;
}
void Log(char* msg) {
	sprintf(buffer, msg, REG_DISPCNT);
	debug(buffer);
}
void LogValue() {
}
